package com.miku.blog.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.miku.blog.domain.*;
import com.miku.blog.domain.dto.ResourceUserDTO;
import com.miku.blog.domain.dto.RoleDTO;
import com.miku.blog.domain.pojo.LoginUser;
import com.miku.blog.domain.pojo.ResponseResult;
import com.miku.blog.domain.pojo.SystemConstance;
import com.miku.blog.domain.vo.PageVo;
import com.miku.blog.domain.vo.RoleVo;
import com.miku.blog.eume.HttpStatusEnum;
import com.miku.blog.exception.SystemException;
import com.miku.blog.mapper.MenuMapper;
import com.miku.blog.mapper.ResourceMapper;
import com.miku.blog.mapper.UserAuthMapper;
import com.miku.blog.service.*;
import com.miku.blog.mapper.RoleMapper;
import com.miku.blog.utils.BeanCopyUtils;
import com.miku.blog.utils.GenerateKeyUtils;
import com.miku.blog.utils.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Miku
 * @description 针对表【tb_role】的数据库操作Service实现
 * @createDate 2022-05-26 17:14:49
 */
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role>
        implements RoleService {

    @Lazy
    @Autowired
    private UserAuthService userAuthService;
    @Autowired
    private ResourceService resourceService;
    @Autowired
    private MenuService menuService;
    @Autowired
    private RedisCache redisCache;


    @Override
    public ResponseResult getRoleList() {
        List<Role> roleList = list();
        List<RoleVo> roleVoList = BeanCopyUtils.copyListByType(roleList, RoleVo.class);
        return ResponseResult.ok().setData(roleVoList);
    }

    @Override
    public boolean updateRole(List<Integer> roleIdList, Integer userId) {

        if (!baseMapper.updateTBUserRoleByUserIdAndRoleId(userId, roleIdList.get(0))) {
            throw new SystemException(HttpStatusEnum.UPDATE_DATA_ERROR);
        }

        String redisCacheKey = redisCache.getCacheObject(GenerateKeyUtils.getRedisUserCacheKey(userId));
        if (StringUtils.hasText(redisCacheKey)) {
            LoginUser loginUser = redisCache.getCacheObject(redisCacheKey);
            Role role = loginUser.getRole();

            if (!role.getId().equals(roleIdList.get(0))) {
                Role newRole = baseMapper.getRoleByUserId(userId);
                loginUser.setRole(newRole).setAuthorities(null);
                updateRedisCacheUserAuth(redisCacheKey, loginUser);
            }
        }
        return true;
    }


    @Override
    public ResponseResult getRoles(Integer current, Integer size, @Nullable String keywords) {

        Page<Role> page = new Page<>(current, size);
        LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.hasText(keywords), Role::getRoleName, keywords)
                .orderByAsc(Role::getCreateTime);
        page(page, queryWrapper);
        List<Role> pageRecords = page.getRecords();
        List<RoleDTO> roleDTOList = null;
        if (Objects.nonNull(pageRecords) && pageRecords.size() > 0) {
            roleDTOList = BeanCopyUtils.copyListByType(pageRecords, RoleDTO.class);
            roleDTOList.stream()
                    .peek(roleDTO -> {
                        Integer roleId = roleDTO.getId();
                        ResourceMapper resourceMapper = (ResourceMapper) resourceService.getBaseMapper();
                        List<Resource> resourceList = resourceMapper.getResourceList(roleId);
                        if (Objects.nonNull(resourceList) && resourceList.size() > 0) {
                            List<Integer> resourceIdList = resourceList.stream()
                                    .map(resource -> resource.getId())
                                    .collect(Collectors.toList());
                            roleDTO.setResourceIdList(resourceIdList);
                        }
                        MenuMapper menuMapper = (MenuMapper) menuService.getBaseMapper();
                        List<Menu> menuList = menuMapper.getMenuListByRoleId(roleId);
                        if (Objects.nonNull(menuList) && menuList.size() > 0) {
                            List<Integer> menuIdList = menuList.stream()
                                    .map(menu -> menu.getId())
                                    .collect(Collectors.toList());
                            roleDTO.setMenuIdList(menuIdList);
                        }
                    }).collect(Collectors.toList());
        }
        PageVo<RoleDTO> pageVo = new PageVo<>(page.getTotal(), roleDTOList);
        return ResponseResult.ok().setData(pageVo);
    }


    @Override
    public ResponseResult disable(Integer roleId, Integer isDisable) {
        if ((!isDisable.equals(SystemConstance.IS_DISABLE) && !isDisable.equals(SystemConstance.NOT_DISABLE))
        ) {
            return new ResponseResult(HttpStatusEnum.PARAM_ERROR);
        }
        LambdaUpdateWrapper<Role> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Role::getId, roleId)
                .set(Role::getIsDisable, isDisable)
                .set(Role::getUpdateTime, new Date());
        if (!update(updateWrapper)) {
            return new ResponseResult(HttpStatusEnum.UPDATE_DATA_ERROR);
        }

        Collection<String> keys = redisCache.keys(SystemConstance.REDIS_LOGINUSER_KEY + SystemConstance.REDIS_PATTERN_KEY);
        keys.stream()
                .peek(key -> {
                    LoginUser loginUser = redisCache.getCacheObject(key);
                    if (Objects.nonNull(loginUser)) {
                        Role role = loginUser.getRole();
                        if (role.getId().equals(roleId)) {
                            role.setIsDisable(isDisable);
                            updateRedisCacheUserAuth(key, loginUser);
                        }
                    }
                })
                .collect(Collectors.toList());

        return ResponseResult.ok();
    }

    @Transactional
    @Override
    public ResponseResult deleteRole(Integer[] roleIdList) {

        UserAuthMapper userAuthMapper = (UserAuthMapper) userAuthService.getBaseMapper();
        for (Integer roleId : roleIdList) {
            List<UserAuth> userAuthList = userAuthMapper.getUserAuthByRoleId(roleId);
            if (Objects.nonNull(userAuthList) && userAuthList.size() > 0) {
                throw new SystemException(HttpStatusEnum.DELETE_ROLE_ERROR);
            }
            baseMapper.deleteTBRoleMenuByRoleId(roleId);
            baseMapper.deleteTBRoleResourceByRoleId(roleId);
            if (!removeById(roleId)) {
                throw new SystemException(HttpStatusEnum.REMOVE_DATA_ERROR);
            }

        }
        return ResponseResult.ok();
    }


    @Transactional
    @Override
    public ResponseResult addRole(RoleDTO roleDTO) {
        if (!StringUtils.hasText(roleDTO.getRoleName()) || !StringUtils.hasText(roleDTO.getRoleLabel())) {
            return new ResponseResult(HttpStatusEnum.PARAM_ERROR);
        }

        List<Integer> menuIdList = roleDTO.getMenuIdList();
        List<Integer> resourceIdList = roleDTO.getResourceIdList();

        Integer roleDTOId = roleDTO.getId();
        if (Objects.isNull(roleDTOId)) {
            //若roleDTOId 为NULL，则表示当前为新增
            Role role = new Role();
            role.setRoleName(roleDTO.getRoleName())
                    .setRoleLabel(roleDTO.getRoleLabel());
            if (!save(role)) {
                return new ResponseResult(HttpStatusEnum.INSERT_DATA_ERROR);
            }
            if (Objects.nonNull(menuIdList) && menuIdList.size() > 0) {
                menuIdList.stream()
                        .peek(menuId -> {
                            if (Objects.isNull(menuService.getById(menuId))
                                    || !baseMapper.insertTBRoleMenuByRoleIdAndMenuId(role.getId(), menuId)
                            ) {
                                throw new SystemException(HttpStatusEnum.INSERT_DATA_ERROR);
                            }
                        }).collect(Collectors.toList());
            }
            if (Objects.nonNull(resourceIdList) && resourceIdList.size() > 0) {
                resourceIdList.stream()
                        .peek(resourceId -> {
                            if (Objects.isNull(resourceService.getById(resourceId))
                                    || !baseMapper.insertTBRoleResourceByRoleIdAndResourceId(role.getId(), resourceId)
                            ) {
                                throw new SystemException(HttpStatusEnum.INSERT_DATA_ERROR);
                            }
                        }).collect(Collectors.toList());
            }
        } else {
            Role role = BeanCopyUtils.copyObjectByType(roleDTO, Role.class);
            Integer isDisable = role.getIsDisable();
            if (!SystemConstance.IS_DISABLE.equals(isDisable)
                    && !SystemConstance.NOT_DISABLE.equals(isDisable)) {
                return new ResponseResult(HttpStatusEnum.PARAM_ERROR);
            }

            if (!updateById(role)) {
                return new ResponseResult(HttpStatusEnum.UPDATE_DATA_ERROR);
            }

            List<Resource> list = null;
            if (Objects.nonNull(menuIdList)) {
                //修改菜单时,资源resourceIdList为NULL     修改资源时,菜单menuIdList为NULL
                MenuMapper menuMapper = (MenuMapper) menuService.getBaseMapper();
                List<Menu> menuList = menuMapper.getMenuListByRoleId(roleDTOId);
                List<Integer> var1 = menuList.stream()
                        .map(menu -> menu.getId())
                        .collect(Collectors.toList());
                if (menuIdList.size() != var1.size() || !menuIdList.containsAll(var1)) {
                    baseMapper.deleteTBRoleMenuByRoleId(roleDTOId);
                    menuIdList.stream()
                            .peek(menuId -> {
                                if (Objects.isNull(menuService.getById(menuId))
                                        || !baseMapper.insertTBRoleMenuByRoleIdAndMenuId(roleDTOId, menuId)
                                ) {
                                    throw new SystemException(HttpStatusEnum.UPDATE_DATA_ERROR);
                                }
                            }).collect(Collectors.toList());
                }
            } else if (Objects.nonNull(resourceIdList)) {
                //修改资源时,菜单menuIdList为NULL     修改菜单时,资源resourceIdList为NULL
                ResourceMapper resourceMapper = (ResourceMapper) resourceService.getBaseMapper();
                List<Resource> resourceList = resourceMapper.getResourceList(roleDTOId);
                List<Integer> var2 = resourceList.stream()
                        .map(Resource::getId)
                        .collect(Collectors.toList());
                if (resourceIdList.size() != var2.size() || !resourceIdList.containsAll(var2)) {
                    baseMapper.deleteTBRoleResourceByRoleId(roleDTOId);
                    list = resourceIdList.stream()
                            .map(resourceId -> {
                                Resource resource = resourceService.getById(resourceId);
                                if (Objects.isNull(resource)
                                        || !baseMapper.insertTBRoleResourceByRoleIdAndResourceId(roleDTOId, resourceId)
                                ) {
                                    throw new SystemException(HttpStatusEnum.UPDATE_DATA_ERROR);
                                }
                                return resource;
                            }).collect(Collectors.toList());
                }
            } else {
                throw new SystemException(HttpStatusEnum.PARAM_ERROR);
            }

            updateRedisUserResource(list, role);

        }
        return ResponseResult.ok();
    }


    @Override
    public Role getRoleByRoleLabel(String label) {

        LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Role::getRoleLabel, label)
                .eq(Role::getIsDisable, SystemConstance.NOT_DISABLE);
        return getOne(queryWrapper);
    }


    private void updateRedisUserResource(List<Resource> resourceList, Role updateRole) {

        Collection<String> keys
                = redisCache.keys(SystemConstance.REDIS_LOGINUSER_KEY + SystemConstance.REDIS_PATTERN_KEY);

        if (Objects.nonNull(keys) && keys.size() > 0) {
            for (String key : keys) {
                LoginUser loginUser = redisCache.getCacheObject(key);
                Role role = loginUser.getRole();
                if (role.getId().equals(updateRole.getId())) {
                    role.setRoleName(updateRole.getRoleName())
                            .setRoleLabel(updateRole.getRoleLabel())
                            .setIsDisable(updateRole.getIsDisable());
                    if (Objects.nonNull(resourceList)) {
                        Map<String, List<ResourceUserDTO>> resourceMap = resourceService.getResourceMap(resourceList);
                        loginUser.setResourceMap(resourceMap);
                    }
                    updateRedisCacheUserAuth(key, loginUser);
                }
            }
        }

    }


    private void updateRedisCacheUserAuth(String redisCacheKey, LoginUser loginUser) {
        redisCache.setCacheObject(redisCacheKey,
                loginUser,
                SystemConstance.REDIS_EXPIRE_TIME,
                SystemConstance.REDIS_TIMEUNIT);

        redisCache.setCacheObject(GenerateKeyUtils.getRedisUserCacheKey(loginUser.getUserAuth().getId()),
                redisCacheKey,
                SystemConstance.REDIS_ID_KEY_EXPIRE_TIME,
                SystemConstance.REDIS_TIMEUNIT);
    }


}




